/*
 * Copyright (c) 2006-2007 Terracotta, Inc. All rights reserved.
 */
package com.google.zahra;

import com.google.zahra.testmanager.Test;
import com.google.zahra.testmanager.TestManager;
import com.google.zahra.webmanager.WebManager;
import com.google.zahra.workmanager.ZahraWorkManager;

import org.terracotta.workmanager.pipemanager.DefaultPipe.Factory;
import org.terracotta.workmanager.pipemanager.DefaultPipe;
import org.terracotta.workmanager.pipemanager.PipeManager;

import org.terracotta.workmanager.queue.ListBasedQueue;

import org.terracotta.workmanager.routing.LoadBalancingRouter;
import org.terracotta.workmanager.routing.RoutableWorkItem;
import org.terracotta.workmanager.routing.Router;

import java.util.Timer;
import java.util.TimerTask;

import commonj.work.WorkException;
import commonj.work.WorkItem;

/**
 * This is the master of the Master-Worker system. It handles checking for
 * pending and completed tests. It also check for pending tests in the {@code
 * TestManager} and schedules it with the {@code WorkManager} when there are
 * empty {@code Pipes}
 *
 * @author arthurkok@google.com
 */
public class Master {

  // Each Queue implementation has a Factory that is used to create a new 
  // instance of that queue
  public static final Factory<RoutableWorkItem<String>, String> PIPE_FACTORY =
      new DefaultPipe.Factory<RoutableWorkItem<String>, String>(
          new ListBasedQueue.Factory<RoutableWorkItem<String>>(1000),
          new ListBasedQueue.Factory<String>(1000));

  /**
   * Main entry point for the master. No arguments required by the master.
   *
   * @param args
   */
  public static void main(String[] args) {
    System.out.println("Starting Master...");

    Master master = new Master();
    master.run();
  }

  // The master contains instances of every type of manager
  private final PipeManager<String> pipeManager;
  private final TestManager testManager;
  private final Router<String> router;
  private final ZahraWorkManager workManager;

  public static int[] listOfOs = new int[] {1, 2, 4};

  /**
   * Constructor. Initializes the managers.
   */
  public Master() {
    pipeManager = new PipeManager<String>(PIPE_FACTORY, null, PipeManager.Initiator.MASTER, 0);
    testManager = new TestManager();
    router = new LoadBalancingRouter<String>(pipeManager);
    workManager = new ZahraWorkManager(router, pipeManager, testManager);

    for (int i : listOfOs) {
      System.out.println(
          "Test pending already OS(" + i + ") : " + testManager.getNumberOfPendingTests(i));
    }

  }

  /**
   * 
   */
  public void run() {

    // Start the pending tests checker thread   
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new PendingTestsChecker(), 0, 100);
    
    // Start the webserver thread
    WebManager webServer = new WebManager(testManager, pipeManager, workManager);
    webServer.start();

    WorkItem item;

    // Loop infinitly
    while (true) {

      try {
        item = workManager.waitForAny(Long.MAX_VALUE);
        Test test = ((Test) item.getResult());

        if (test.failed) {
          System.out.println(
              String.format("%s with ID: %d and " + (char) 27 + "[31mFAILED" + (char) 27 + "[m",
                  item, test.jobID));

        //If the test has more retries
          if (test.retries >= 1) {
            System.out.println("Re-adding JobID:\t\t" + test.jobID);
            testManager.addTestCase(test.jobID, test.file, test.name, test.os, test.retries - 1);
          } else {
            System.out.println(
                (char) 27 + "[31mNo more retries JobID:\t\t" + (char) 27 + "[m" + test.jobID);
          }

        } else {
          System.out.println(
              String.format("%s with ID: %d and " + (char) 27 + "[92mPASSED" + (char) 27 + "[m",
                  item, test.jobID));
        }
      } catch (WorkException e) {
        e.printStackTrace();
      } catch (InterruptedException e1) {
        Thread.interrupted();
        e1.printStackTrace();
      }
    }

  }

  class PendingTestsChecker extends TimerTask {
    @Override
    public void run() {

      // Check if there are pending tests for a specific OS, if there are, it 
      // then check if there are empty pipes (i.e. idle workers). If there are,
      // if will schedule the test. The WorkManager and Router will handle the
      // routing of the test to the idle worker.
      for (int os : listOfOs) {
        while (testManager.hasPendingTests(os) && pipeManager.hasEmptyPipeForOs(os)) {
          RoutableWorkItem<String> workItem = testManager.getNextPendingTest(os);
          workManager.schedule(workItem, null);
        }
      }
    }
  }

}
